/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.core.action.base;

import com.inspur.edp.bef.api.be.IBEManager;
import com.inspur.edp.commonmodel.core.variable.VarBufferManager;
import com.inspur.edp.bef.core.be.BEManager;
import com.inspur.edp.bef.core.be.BEManagerContext;
import com.inspur.edp.bef.core.session.BEFuncSessionBase;
import com.inspur.edp.bef.core.session.FuncSession;
import com.inspur.edp.bef.core.session.FuncSessionManager;
import com.inspur.edp.bef.spi.action.AbstractManagerAction;
import com.inspur.edp.cef.api.dataType.valueObj.ICefValueObject;
import com.inspur.edp.cef.api.message.AggregateBizMsgException;
import com.inspur.edp.cef.api.message.IBizMessage;
import com.inspur.edp.cef.api.session.ICefSessionItem;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ValueObjModifyChangeDetail;
import com.inspur.edp.cef.spi.manager.MgrActionExecutor;
import com.inspur.edp.cef.variable.api.manager.IVariableManager;
import com.inspur.edp.commonmodel.core.session.distributed.SessionEditToken;
import java.util.Collections;
import java.util.List;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BEMgrActionExecutor<T> extends MgrActionExecutor<T> {

   private final FuncSession currentSession;

   public BEMgrActionExecutor(FuncSession session){
      this.currentSession = session;
   }

   private boolean hasPushStack = false;

   private SessionEditToken sessionEditor;

   public BEManagerContext getBEMgrContext() {
      return (BEManagerContext) super.getContext();
   }

   //region HandleException
   @Override
   protected boolean onHandleException(RuntimeException e) {
      if (isLastNode) {
         for (IBEManager mgr : mgrs) {
            BEFuncSessionBase sessionItem = currentSession.getFuncSessionItem(mgr.getBEType());
            if(sessionItem == null)
               continue;
            ((BEManager) mgr).clearCurrentChanges();
            sessionItem.getLockServiceItem().rejectCurrentLocks();
         }
      }
      return super.onHandleException(e);
   }
   //endregion

   //region OnBefore
//   private Lock wirteLock;
   private boolean isLastNode;
   private boolean isOutmost;
   private List<IBEManager> mgrs = Collections.emptyList();
   @Override
   protected void onBeforeExecute() {
      //最外层ManagerAction要清空Response.Message
      isLastNode = ActionStack.isEmpty();
      isOutmost = ActionStack.isOutmost();
      ActionStack.push(currentSession, "AbstractManagerAction", getBEMgrContext().getBEManager());
      mgrs = ActionStack.getActionManagers();
      hasPushStack = true;
      if (isLastNode) {
         for(ICefSessionItem item : currentSession.getSessionItems().values()) {
            if(item instanceof BEFuncSessionBase)
               ((BEFuncSessionBase)item).getResponseContext().clear();
         }
         sessionEditor = FuncSessionManager.getCurrent().beginEdit(currentSession, false);
      }
      super.onBeforeExecute();
   }

   @Override
   protected void onAfterExecute() {
      if(hasError()){
         List<IBizMessage> msgs = ((BEManager) getBEMgrContext().getBEManager()).getResponseContext().getMessages();
         throw new AggregateBizMsgException(msgs);
      }

      super.onAfterExecute();
      if(!(getAction() instanceof AbstractManagerAction&&((AbstractManagerAction<T>) getAction()).isActionReadonly()))
      {
         mergeVariableChange();
         mergeChangeAndLocks();
      }
   }

   private boolean isCurrentBEType(){
      return getBEMgrContext().getBEManager().getBEType().equals(ActionStack.getFirstBEType());
   }

   private boolean hasError() {
      return ((BEManager) getBEMgrContext().getBEManager()).getResponseContext().hasErrorMessage();
   }

   private void mergeVariableChange() {
      IVariableManager varMgr = getBEMgrContext().getVariableManager();
      VarBufferManager varBufferManager = getBEMgrContext().getVarBufferManager();
      if (varMgr == null) {
         return;
      }

      Object tempVar = varBufferManager.Listener().getChange();
      ValueObjModifyChangeDetail change = (ValueObjModifyChangeDetail) (
          (tempVar instanceof ValueObjModifyChangeDetail) ? tempVar : null);
      if (change == null || change.getPropertyChanges().isEmpty()) {
         return;
      }

      //变量内部变更
      ((BEManager) getBEMgrContext().getBEManager()).getResponseContext().mergeVarInnerChange(change);
      varBufferManager.Listener().clear();
      ICefValueObject varEntity = varMgr.createValueObject(varBufferManager.getCurrentData());
      varEntity.afterModifyDeterminate(change);
      IChangeDetail dtmChange = varBufferManager.Listener().getChange();
      varBufferManager.Listener().clear();
      if (dtmChange != null) {
         ((BEManager) getBEMgrContext().getBEManager()).getResponseContext()
             .mergeVarInnerChange(dtmChange);
      }
   }

   private void mergeChangeAndLocks() {
      if (!isLastNode) {
         return;
      }
      boolean failed =  mgrs.stream()
          .anyMatch(mgr -> ((BEManager) mgr).getResponseContext().hasErrorMessage());
      mergeCurrentChanges(failed);
      mergeCurrentLocks(failed);
   }

   private void mergeCurrentLocks(boolean failed) {
      for (IBEManager beMgr : mgrs) {
         if (failed) {
            ((BEManager) beMgr).getSessionItem().getLockServiceItem().rejectCurrentLocks();
         } else {
            ((BEManager) beMgr).getSessionItem().getLockServiceItem().acceptCurrentLocks();
         }
      }
   }

   private void mergeCurrentChanges(boolean failed) {
      for (IBEManager beMgr : mgrs) {
         if (failed) {
            ((BEManager) beMgr).clearCurrentChanges();
         } else {
            ((BEManager) beMgr).mergeCurrentDataToTransaction();
         }
      }
   }
   //endregion

   //region OnFinally
   @Override
   protected void onFinally() {
      try {
         if (isLastNode) {
            actionClear(currentSession);
            if(sessionEditor != null) {
               FuncSessionManager.getCurrent().endEdit(sessionEditor);
            }
         }
      } finally {
         popActionStack();
      }
   }

   private void popActionStack() {
      if (!hasPushStack) {
         return;
      }

      if(isOutmost) {
         ActionStack.clear();
         return;
      }

      boolean changed = ActionUtil.checkSessionChanged(currentSession.getSessionId(),
          getBEMgrContext().getBEManager().getBEType(),ActionUtil.CATEGORY_MANAGER_ACTION, getAction());
      if(!changed) {
         hasPushStack = false;
         ActionStack.pop();
      }
   }

   public static void actionClear(FuncSession session) {
      //FuncSession session = FuncSessionManager.getCurrentSession();
//      session.getCallContext().clear();
      for(ICefSessionItem item : session.getSessionItems().values()) {
         if(item instanceof BEFuncSessionBase)
            ((BEFuncSessionBase)item).getCallContext().clear();
      }
   }
   //endregion
}
